﻿#define _CRT_SECURE_NO_WARNINGS 1
#include"snack.h"

//设置光标的位置
void SetPos(short x, short y)
{
	COORD pos = { x, y };
	HANDLE hOutput = NULL;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置标准输出上光标的位置为
	SetConsoleCursorPosition(hOutput, pos);
}

//打印方框
void Board()
{
	//打印方框
	int i = 0;
	SetPos(0, 0);
	for (i = 0; i < 59; i++)
	{
		wprintf(L"%lc",WALL);
	}
	SetPos(0, 26);
	for (i = 0; i < 59; i++)
	{
		wprintf(L"%lc", WALL);
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	for (i = 1; i < 26; i++)
	{
		SetPos(58, i);
		wprintf(L"%lc",WALL);
	}

	SetPos(10, 27);
}

void _Welcom()
{
	//设置开始界面
//两个初始界面
	SetPos(50, 15);
	printf("贪吃蛇游戏\n");
	SetPos(50, 28);
	system("pause");
	system("cls");

	SetPos(36, 14);
	printf("游戏说明:");
	SetPos(36, 15);
	printf("按 ↑. ↓. ←. →. 键操纵移动 收集 ★ 获得更高分");
	SetPos(36, 16);
	printf("请勿撞到墙或自己  按ESC退出游戏  空格暂停游戏");
	SetPos(50, 28);
	system("pause");
	system("cls");
}

void Snack_Init(Snack* ps)
{
	ps->_phead = NULL;
	//创建最初的蛇身
    //链表的方式 头插
	SnackNode* cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur = (SnackNode*)malloc(sizeof(SnackNode));
		if (cur == NULL)
		{
			perror("Snack_Init();;malloc()");
			return;
		}

		cur->next = NULL;
		cur->x = 24 + 2 * i;
		cur->y = 5;

		if (ps->_phead == NULL)
		{
			ps->_phead = cur;
		}
		else
		{
			cur->next = ps->_phead;
			ps->_phead = cur;
		}
	}

	//打印蛇身
	cur = ps->_phead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//设置相关的信息
	ps->_dir = RIGHT;
	ps->_one_food = 10;
	ps->_score = 0;
	ps->_sleep_time = 200;
	ps->_sta = OK;

}

void Food_Init(Snack* ps)
{
	SnackNode* Food = malloc(sizeof(SnackNode));
	if (Food == NULL)
	{
		perror("Food_Init()::malloc()");
		return;
	}

	//食物要在方框中
	do
	{
	again:
		Food->x = rand() % 55 + 2;
		Food->y = rand() % 25 + 1;
	} while (Food->x % 2 != 0);

	//食物不能与蛇身重合
	SnackNode* cur = ps->_phead;
	do
	{
		if (Food->x == cur->x&&Food->y==cur->y)
			goto again;
		cur = cur->next;
	} while (cur);

	ps->_pfood = Food;

	Food->next = NULL;
	SetPos(ps->_pfood->x, ps->_pfood->y);
	wprintf(L"%lc", FOOD);

}

void GameStart(Snack* ps)
{
	//打印开始界面

	setlocale(LC_ALL, "");
	//设置界面大小
	system("mode con cols=110 lines=30");
	system("title 贪吃蛇");
	//获取标准输出的句柄
	HANDLE hOutput = NULL;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    //获取控制台光标信息
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);
	//隐藏控制台光标
	CursorInfo.bVisible = false; 
	//设置控制台光标状态
	SetConsoleCursorInfo(hOutput, &CursorInfo);

	//欢迎界面
	_Welcom();
	//游戏的范围
	Board();
	//蛇信息的设置
	Snack_Init(ps);
    //食物的设置
	Food_Init(ps);
}


void pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
			break;
     }

}

void GameTips()
{
	SetPos(61,15);
	wprintf(L"%ls",L"按 ↑. ↓. ←. →. 键操纵移动 收集 ★ 获得更高分");
	SetPos(61, 16);
	wprintf(L"%ls", L"按 F1 加速食物分数增加  F2 减速食物分数减少");
	SetPos(61, 17);
	wprintf(L"%ls", L"请勿撞到墙或自己  按ESC退出游戏  空格暂停游戏");

	SetPos(61, 20);
	wprintf(L"%ls", L"ZhangJ制作");
}

void ScoreTip(Snack* ps)
{
	SetPos(61, 10);
	wprintf(L"总分：%d",ps->_score);
	SetPos(61, 11);
	wprintf(L"单个食物分：%2d", ps->_one_food);
}

bool NextIsFood(SnackNode* next, Snack* ps)
{
	if (next->x == ps->_pfood->x && next->y == ps->_pfood->y)
		return true;

	return false;
}

void EatFood(SnackNode* next, Snack* ps)
{
  //下一个是食物
	//把食物链接到蛇身上
	//释放next节点

	ps->_pfood->next = ps->_phead;
	ps->_phead = ps->_pfood;

	free(next);
	next = NULL;
    
	//打印每个节点
	SnackNode* cur = ps->_phead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	//创建新的食物
	Food_Init(ps);
}

void KILL_Self(Snack* ps)
{
	SnackNode* cur = ps->_phead->next;
	while (cur)
	{
		if (ps->_phead->x == cur->x && ps->_phead->y == cur->y)
		{
          
			ps->_sta = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}

void KILL_WALL(SnackNode* next, Snack* ps)
{
	if (next->x == 0 || next->x == 58 || next->y == 0 || next->y == 26)
	{
		printf("撞墙死亡");
		ps->_sta = KILL_BY_WALL;
	}
}

void NoEatFood(SnackNode* next, Snack* ps)
{
   //如果不是食物
	//把next头插进链表中
	//并且释放最后一个节点

	//同时要把之前输出的蛇身覆盖

	next->next = ps->_phead;
	ps->_phead = next;

	SnackNode* cur = ps->_phead;
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	wprintf(L"  ");

	SnackNode* del = cur->next;
	cur->next = NULL;
	free(del);
}

void SnackMove(Snack* ps)
{
	SnackNode* next = malloc(sizeof(SnackNode));
	if (next == NULL)
	{
		perror("SnackMove()::malloc()");
		return;
	}

	switch (ps->_dir)
	{
	case UP:
		next->x = ps->_phead->x;
		next->y = ps->_phead->y - 1;
		break;
	case DOWN:
		next->x = ps->_phead->x;
		next->y = ps->_phead->y + 1;
		break;
	case LEFT:
		next->x = ps->_phead->x-2;
		next->y = ps->_phead->y ;
		break;
	case RIGHT:
		next->x = ps->_phead->x+2;
		next->y = ps->_phead->y;
		break;
	}

	if (NextIsFood(next, ps))
	{
		EatFood(next,ps);
		ps->_score += ps->_one_food;
	}
	else
	{
		NoEatFood(next, ps);
	}

	KILL_Self(ps);

	//是否撞墙
	KILL_WALL(next,ps);

}

void GameRun(Snack* ps)
{

	//判断按键的情况
	do
	{
		//游戏右侧的提示信息
		GameTips();

		//分数提示
		ScoreTip(ps);
		if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
		{
			ps->_dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
		{
			ps->_dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
		{
			ps->_dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
		{
			ps->_dir = RIGHT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出
			ps->_sta = END_NORMAL;
		}
		else if (KEY_PRESS(0x70))
		{
			//加速
			//Sleep时间减少
			//单个食物增加
			if (ps->_sleep_time > 100)
			{
				ps->_sleep_time -= 20;
				ps->_one_food += 2;
			}
		}
		else if (KEY_PRESS(0x71))
		{
			//减速
			if (ps->_one_food > 2)
			{
				ps->_sleep_time += 20;
				ps->_one_food -= 2;
			}
		}

		//循环一次蛇走一步
		SnackMove(ps);
		Sleep(ps->_sleep_time);

	} while (ps->_sta==OK);
}


void GameOver(Snack* ps)
{
	//释放食物
	free(ps->_pfood);
	//释放链表中的节点
	SnackNode* cur = ps->_phead;
	while (cur)
	{
		SnackNode* del = cur;
		cur = cur->next;
		free(del);
	}

}